package com.example.net.websocket;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;


import com.example.baseutils.Constance;
import com.example.baseutils.PreferncesUtilKt;
import com.example.baseutils.application.BaseApplication;

import com.example.net.websocket.bean.ChatData;
import com.example.net.websocket.bean.ChatNewData;
import com.example.net.websocket.service.WebSocketService;
import com.google.gson.Gson;

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.extensions.IExtension;
import org.java_websocket.handshake.ServerHandshake;
import org.java_websocket.protocols.IProtocol;
import org.java_websocket.protocols.Protocol;

import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Logger;


/**
 * Created by xiaoyuren on 2018/2/27.
 * 项目名称：didano-robot
 * 类描述：websocket管理类 具有连接、断开、接收信息、发送信息功能
 * company：www.didano.cn
 * email：vin.qin@didano.cn
 * 创建时间：2018/2/27 13:52
 */

public class WSManager {
    private static final String TAG = "WSManager";
    /**
     * 重连最小时间间隔2s
     */
    private final int RETRY_MIN_INTERVAL = 3000;

    /**
     * 重连最大时间间隔
     */
    private final int RETRY_MAX_INTERVAL = 5000;

    /**
     * 检测网络状态时间间隔 5s
     */
    private final int CHECK_NETWORK_INTERVAL = 5000;

    /**
     * 重连次数
     */
    private int reconnectCount = 0;

    /**
     * 延时任务
     */
    private Handler handler = new Handler();

    /**
     * websocket客户端
     */
    private WebSocketClient client;
    /**
     * 状态
     */
    private WSStatus wsStatus = WSStatus.CONNECTTING;
    private static volatile WSManager instance;

    private boolean isInitOk = false;

    private Handler mainHandler = new Handler(Looper.getMainLooper());

    public WSStatus getWsStatus() {
        return wsStatus;

    }

    public void setWsStatus(WSStatus wsStatus) {
        this.wsStatus = wsStatus;
        mainHandler.post(() -> {
            for (ChatCallBack chatCallBack : callBackList) {
                chatCallBack.stateChange(wsStatus);
            }
        });

    }

    private List<ChatCallBack> callBackList = new CopyOnWriteArrayList<ChatCallBack>();

    public void addCallBack(ChatCallBack callBack) {
        callBackList.add(callBack);
    }

    public void reStartSocket(Context context) {
        WebSocketService.startWebSocket(context);
    }

    public void removeCallback(ChatCallBack callBack) {
        callBackList.remove(callBack);
    }

    //    private String sessionId = "";
    private String uid = "";
    private String robotId = "";

    public interface ChatCallBack {
        void stateChange(WSStatus wsStatus);

        void chatResponse(String msg);
    }

    private WSManager() {
    }

    /**
     * 获取单例
     *
     * @return
     */
    public static WSManager getInstance() {
        if (instance == null) {
            synchronized (WSManager.class) {
                if (instance == null)
                    instance = new WSManager();
            }
        }
        return instance;
    }

    /**
     * 初始化websocket
     *
     * @return
     */
    public boolean init() {
        Log.i(TAG, "-----------websocket init-----------------: ");
        Log.i(TAG, "-----------isInitOk: " + isInitOk + "------------");
        /**
         * 已经初始化了，无需再次初始化
         */
        if (isInitOk) {
            return isInitOk;
        } else {
            Log.i(TAG, "********************初始化:    " + isInitOk);
            isInitOk = connect();

            return isInitOk;
        }
    }

    public boolean connect() {
        try {
            if (client != null) {
                client = null;
            }
            Log.i(TAG, "----------client——before: " + client);
            String token = PreferncesUtilKt.getMySharedPreferences().getString(Constance.Authorization, "");
            uid = PreferncesUtilKt.getMySharedPreferences().getString(Constance.U_ID, "");
//            uid = "1757647080962265088";//TODO 临时使用uid
            robotId = PreferncesUtilKt.getMySharedPreferences().getString(Constance.ROBOT_ID, "");
            Log.i(TAG, "token: " + token + "  " + " ,  uid:  " + uid + "  ,  robotId:  " + robotId);
            Map<String, String> httpHeaders = new HashMap<>();
            httpHeaders.put("Authorization", token);
            httpHeaders.put("Sec-WebSocket-Protocol", token);
//            httpHeaders.put("uid", uid);
//            httpHeaders.put("robotId", robotId);
            ArrayList<IProtocol> protocols = new ArrayList<IProtocol>();
            protocols.add(new Protocol(token));
            client = new WSClient(new URI(WSConstant.BASE_API), httpHeaders,protocols);
            Log.i(TAG, "connect:BASE_API " + WSConstant.BASE_API);
            Log.i(TAG, "----------client——after " + client);
            setWsStatus(WSStatus.CONNECTTING);
            client.connect();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            Log.e(TAG, "websocket init error" );
//            Logger.getAnonymousLogger("websocket init error");
            return false;
        }
    }

    private boolean isNetworkConnected() {
        Context context = BaseApplication.instance;
        ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = manager.getActiveNetworkInfo();
        if (networkInfo != null)
            return networkInfo.isAvailable();
        return false;
    }

    public void reconnectAfter() {

        if (!isNetworkConnected()) {
            /**
             * 网络连接不可用，那么暂停一段时间再请求
             */
            handler.removeCallbacks(checkNetworkTask);
            handler.postDelayed(checkNetworkTask, CHECK_NETWORK_INTERVAL);

            return;
        }

        /**
         * 连接已经断开，且不是正在重连
         */
        if (client != null && !client.isOpen() && wsStatus != WSStatus.CONNECTTING) {
            setWsStatus(WSStatus.CONNECTTING);
            reconnectCount++;
            int reconnectTime = RETRY_MIN_INTERVAL;
            if (reconnectCount > 3) {
                int temp = RETRY_MIN_INTERVAL * (reconnectCount - 2);
                reconnectTime = temp > RETRY_MAX_INTERVAL ? RETRY_MAX_INTERVAL : temp;
            }
            handler.removeCallbacks(reconnectTask);
            handler.postDelayed(reconnectTask, reconnectTime);
        }
    }

    private Runnable reconnectTask = new Runnable() {
        @Override
        public void run() {
            Log.i(TAG, "reconnectAfter to websocket server: ");
            if (isInitOk)
                connect();
        }
    };

    private Runnable checkNetworkTask = new Runnable() {
        @Override
        public void run() {
            Log.i(TAG, "network is unavailable websocket check status ");
            reconnectAfter();
        }
    };

    /**
     * 取消重连
     */
    public void cancelReconnect() {
        reconnectCount = 0;
        handler.removeCallbacks(reconnectTask);
        handler.removeCallbacks(checkNetworkTask);
    }

    /**
     * 断开连接
     *
     * @return
     */
    public boolean disconnect() {
        isInitOk = false;
        client.close();
        cancelReconnect();
        return true;
    }

    /**
     * 非阻塞发送方法
     * 有可能多线程调用 加锁
     *
     * @param message
     */
    public synchronized void send(String message) {
        if (client != null && client.isOpen()) {
            Log.i(TAG, "send: " + message);
            client.send(message);
        }
    }


    public WSStatus sendRequest(String msgChat) {
        if (TextUtils.isEmpty(msgChat)) {
            return wsStatus;
        } else {
            msgChat.length();
        }
        ChatNewData data = new ChatNewData();
        ChatNewData.MessageBean messageBean=new ChatNewData.MessageBean();
        messageBean.setChatText(msgChat);
        messageBean.setChatType(0);
        data.setMessage(messageBean);
//        data.setMsgType("chat");
        data.setType("chat");
        data.setTime(System.currentTimeMillis());
        String json = new Gson().toJson(data);
        Log.i(TAG, "sendRequest: " + json);
        LocationDataCacher.getInstance().PushCacher(json);
        return wsStatus;
    }

    class WSClient extends WebSocketClient {

        public WSClient(URI serverUri) {
            super(serverUri);

        }

        public WSClient(URI serverUri, Map<String, String> httpHeaders, List<IProtocol> protocols) {
            super(serverUri, new Draft_6455(Collections.<IExtension>emptyList(), protocols), httpHeaders);
        }

        /**
         * Called after an opening handshake has been performed and the given websocket is ready to be written on.
         *
         * @param handshakedata The handshake of the websocket instance
         */
        @Override
        public void onOpen(ServerHandshake handshakedata) {
            Log.i(TAG, "websocket开启成功: ");
            //连接建立成功
            cancelReconnect();
            setWsStatus(WSStatus.CONNECT_SUCCESS);

        }

        @Override
        public void onMessage(String message) {
            if (message != null)
                dispatchMessage(message);
        }

        @Override
        public void onClose(int code, String reason, boolean remote) {
//            Logger.i("ws connect close");
            Log.e(TAG, "code : " + code + " reason : " + reason + " remote : " + remote);
            setWsStatus(WSStatus.CONNECT_FAIL);
            reconnectAfter();

        }

        /**
         * Called when errors occurs. If an error causes the websocket connection to fail {@link #onClose(int, String, boolean)} will be called additionally.<br>
         * This method will be called primarily because of IO or protocol errors.<br>
         * If the given exception is an RuntimeException that probably means that you encountered a bug.<br>
         *
         * @param ex The exception causing this error
         **/
        @Override
        public void onError(Exception ex) {
            Log.e(TAG, "ws connect error  " + ex.getMessage());
            setWsStatus(WSStatus.CONNECT_FAIL);
            reconnectAfter();
        }
    }

    private Gson gson = new Gson();

    private String lastMsg = "";

    /**
     * 分发信息给相应的模块
     *
     * @param msg
     */
    public void dispatchMessage(String msg) {
        Log.i(TAG, "receive push message:" + msg);
        if (lastMsg.equals(msg))
            return;
        lastMsg = msg;
        try {
            /**
             * 先回复该命令，再继续其它操作
             */
            Gson gson = new Gson();
            ChatNewData response = gson.fromJson(msg, ChatNewData.class);
            Log.i(TAG, "dispatchMessage: response" + response);
            if (callBackList.isEmpty() || response == null) {
                return;
            }
                mainHandler.post(() -> {
                    for (ChatCallBack chatCallBack : callBackList) {
                        Log.i(TAG, "dispatchMessage:carCallBack ");
                        chatCallBack.chatResponse(response.getMessage().getChatText());
                    }
                });
//            send(response);

//            if (cmd.equals(UPDATE_SOFTWARE_VERSION)) {
//                Log.i(TAG, "check app version...");
//                /**
//                 * 检查版本更新
//                 */
//                UpVersionService.Companion.startUpVersionService(BaseApplication.instance);
//            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String msg = "";

    public void sendBack() {
        synchronized (msg) {
            send(msg);
        }
    }
}
